#include "U2Frustum.h"

#include "U2Exception.h"
#include "U2HardwareBufferManager.h"
#include "U2HardwareBuffer.h"
#include "U2Root.h"
#include "U2RenderSystem.h"



U2EG_NAMESPACE_USING



const u2real U2Frustum::INFINITE_FAR_PLANE_ADJUST = 0.00001;


//-----------------------------------------------------------------------
U2Frustum::U2Frustum(const U2String& name) 
: mOrientationMode(OR_DEGREE_0)
, mRecalcFrustum(true)
, mRecalcFrustumPlanes(true)
, mRecalcWorldSpaceCorners(true)
, mRecalcVertexData(true)
, mProjType(PT_ORTHOGRAPHIC) 
, mFOVy(Radian(U2Math::PI/4.0f))
, mFarDist(100000.0f)
, mNearDist(100.0f)
, mAspect(1.33333333333333f)
, mOrthoHeight(1000)
, mFrustumOffset(U2Vector2::ZERO)
, mFocalLength(1.0f)
, mFrustumExtentsManuallySet(false)
, mLeft(0.0f)
, mRight(0.0f)
, mTop(0.0f)
, mBottom(0.0f)
, mLastParentOrientation(U2Quaternion::IDENTITY)
, mLastParentPosition(U2Vector3::ZERO)
{

}

//-----------------------------------------------------------------------
U2Frustum::~U2Frustum()
{
}

//---------------------------------------------------------------------
void U2Frustum::setOrientationMode(OrientationMode orientationMode)
{
#if U2_NO_VIEWPORT_ORIENTATIONMODE != 0
    U2_EXCEPT(U2Exception::ERR_NOT_IMPLEMENTED,
        "Setting Frustrum orientation mode is not supported",
        __FUNCTION__);
#endif
    mOrientationMode = orientationMode;
    /**********************@@@@@@@@@@@@@@@@@@@@@
    invalidateFrustum();
    */
}

//---------------------------------------------------------------------
OrientationMode U2Frustum::getOrientationMode() const
{
#if U2_NO_VIEWPORT_ORIENTATIONMODE != 0
    U2_EXCEPT(U2Exception::ERR_NOT_IMPLEMENTED,
        "Getting Frustrum orientation mode is not supported",
        __FUNCTION__);
#endif
    return mOrientationMode;
}

//-----------------------------------------------------------------------
u2real U2Frustum::getAspectRatio(void) const
{
    return mAspect;
}

//-----------------------------------------------------------------------
void U2Frustum::setAspectRatio(u2real r)
{
    mAspect = r;
    /**********************@@@@@@@@@@@@@@@@@@@@@
    invalidateFrustum();
    */
}

//-----------------------------------------------------------------------
const U2Matrix4& U2Frustum::getProjectionMatrixRS(void) const
{

    updateFrustum();

    return mProjMatrixRS;
}

//-----------------------------------------------------------------------
void U2Frustum::updateFrustum(void) const
{
    if (isFrustumOutOfDate())
    {
        updateFrustumImpl();
    }
}

//-----------------------------------------------------------------------
bool U2Frustum::isFrustumOutOfDate(void) const
{
    return mRecalcFrustum;
}

//-----------------------------------------------------------------------
void U2Frustum::calcProjectionParameters(u2real& left, u2real& right, u2real& bottom, u2real& top) const
{
    /**********************@@@@@@@@@@@@@@@@@@@@@
    if (mCustomProjMatrix)
    {
        // Convert clipspace corners to camera space
        U2Matrix4 invProj = mProjMatrix.inverse();
        U2Vector3 topLeft(-0.5f, 0.5f, 0.0f);
        U2Vector3 bottomRight(0.5f, -0.5f, 0.0f);

        topLeft = invProj * topLeft;
        bottomRight = invProj * bottomRight;

        left = topLeft.x;
        top = topLeft.y;
        right = bottomRight.x;
        bottom = bottomRight.y;

    }
    else
    */
    {
        if (mFrustumExtentsManuallySet)
        {
            left = mLeft;
            right = mRight;
            top = mTop;
            bottom = mBottom;
        }
        // Calculate general projection parameters
        else if (mProjType == PT_PERSPECTIVE)
        {
        }
        else
        {
            // Unknown how to apply frustum offset to orthographic camera, just ignore here
            u2real half_w = getOrthoWindowWidth() * 0.5f;
            u2real half_h = getOrthoWindowHeight() * 0.5f;

            left   = - half_w;
            right  = + half_w;
            bottom = - half_h;
            top    = + half_h;

            mLeft = left;
            mRight = right;
            mTop = top;
            mBottom = bottom;
        }

    }
}

//-----------------------------------------------------------------------
void U2Frustum::updateFrustumImpl(void) const
{
	// Common calcs
	u2real left, right, bottom, top;

#if U2_NO_VIEWPORT_ORIENTATIONMODE == 0
    if (mOrientationMode != OR_PORTRAIT)
        calcProjectionParameters(bottom, top, left, right);
    else
#endif
        calcProjectionParameters(left, right, bottom, top);

	//if (!mCustomProjMatrix)
	{

		// The code below will dealing with general projection 
		// parameters, similar glFrustum and glOrtho.
		// Doesn't optimise manually except division operator, so the 
		// code more self-explaining.

		u2real inv_w = 1 / (right - left);
		u2real inv_h = 1 / (top - bottom);
		u2real inv_d = 1 / (mFarDist - mNearDist);

		// Recalc if frustum params changed
		if (mProjType == PT_PERSPECTIVE)
		{
		} // perspective
		else if (mProjType == PT_ORTHOGRAPHIC)
		{
			u2real A = 2 * inv_w;
			u2real B = 2 * inv_h;
			u2real C = - (right + left) * inv_w;
			u2real D = - (top + bottom) * inv_h;
			u2real q, qn;
			if (mFarDist == 0)
			{
				// Can not do infinite far plane here, avoid divided zero only
				q = - U2Frustum::INFINITE_FAR_PLANE_ADJUST / mNearDist;
				qn = - U2Frustum::INFINITE_FAR_PLANE_ADJUST - 1;
			}
			else
			{
				q = - 2 * inv_d;
				qn = - (mFarDist + mNearDist)  * inv_d;
			}

			// NB: This creates 'uniform' orthographic projection matrix,
			// which depth range [-1,1], right-handed rules
			//
			// [ A   0   0   C  ]
			// [ 0   B   0   D  ]
			// [ 0   0   q   qn ]
			// [ 0   0   0   1  ]
			//
			// A = 2 * / (right - left)
			// B = 2 * / (top - bottom)
			// C = - (right + left) / (right - left)
			// D = - (top + bottom) / (top - bottom)
			// q = - 2 / (far - near)
			// qn = - (far + near) / (far - near)

			mProjMatrix = U2Matrix4::ZERO;
			mProjMatrix[0][0] = A;
			mProjMatrix[0][3] = C;
			mProjMatrix[1][1] = B;
			mProjMatrix[1][3] = D;
			mProjMatrix[2][2] = q;
			mProjMatrix[2][3] = qn;
			mProjMatrix[3][3] = 1;
		} // ortho            
	} // !mCustomProjMatrix

#if U2_NO_VIEWPORT_ORIENTATIONMODE == 0
    // Deal with orientation mode
    mProjMatrix = mProjMatrix * Quaternion(Degree(mOrientationMode * 90.f), U2Vector3::UNIT_Z);
#endif

	U2RenderSystem* renderSystem = U2Root::getSingleton().getRenderSystem();
	// API specific
	renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRS);
	// API specific for Gpu Programs
	renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRSDepth, true);


	// Calculate bounding box (local)
	// Box is from 0, down -Z, max dimensions as determined from far plane
	// If infinite view frustum just pick a far value
	u2real farDist = (mFarDist == 0) ? 100000 : mFarDist;
	// Near plane bounds
	U2Vector3 min(left, bottom, -farDist);
	U2Vector3 max(right, top, 0);

    /**********************@@@@@@@@@@@@@@@@@@@@@
	if (mCustomProjMatrix)
	{
		// Some custom projection matrices can have unusual inverted settings
		// So make sure the AABB is the right way around to start with
		U2Vector3 tmp = min;
		min.makeFloor(max);
		max.makeCeil(tmp);
	}
    */

	mBoundingBox.setExtents(min, max);

	mRecalcFrustum = false;

	// Signal to update frustum clipping planes
	mRecalcFrustumPlanes = true;
}

//---------------------------------------------------------------------
void U2Frustum::setFrustumExtents(u2real left, u2real right, u2real top, u2real bottom)
{
    mFrustumExtentsManuallySet = true;
    mLeft = left;
    mRight = right;
    mTop = top;
    mBottom = bottom;

    invalidateFrustum();
}

//---------------------------------------------------------------------
void U2Frustum::resetFrustumExtents()
{
    mFrustumExtentsManuallySet = false;
    invalidateFrustum();
}

//---------------------------------------------------------------------
void U2Frustum::getFrustumExtents(u2real& outleft, u2real& outright, u2real& outtop, u2real& outbottom) const
{
    updateFrustum();
    outleft = mLeft;
    outright = mRight;
    outtop = mTop;
    outbottom = mBottom;
}

//---------------------------------------------------------------------
void U2Frustum::setOrthoWindow(u2real w, u2real h)
{
    mOrthoHeight = h;
    mAspect = w / h;
    invalidateFrustum();
}
//---------------------------------------------------------------------
void U2Frustum::setOrthoWindowHeight(u2real h)
{
    mOrthoHeight = h;
    invalidateFrustum();
}
//---------------------------------------------------------------------
void U2Frustum::setOrthoWindowWidth(u2real w)
{
    mOrthoHeight = w / mAspect;
    invalidateFrustum();
}
//---------------------------------------------------------------------
u2real U2Frustum::getOrthoWindowHeight() const
{
    return mOrthoHeight;
}
//---------------------------------------------------------------------
u2real U2Frustum::getOrthoWindowWidth() const
{
    return mOrthoHeight * mAspect;	
}

// -------------------------------------------------------------------
void U2Frustum::invalidateView() const
{
    mRecalcView = true;
    mRecalcFrustumPlanes = true;
    mRecalcWorldSpaceCorners = true;
}
// -------------------------------------------------------------------
void U2Frustum::invalidateFrustum() const
{
    mRecalcFrustum = true;
    mRecalcFrustumPlanes = true;
    mRecalcWorldSpaceCorners = true;
    mRecalcVertexData = true;
}

//-----------------------------------------------------------------------
void U2Frustum::updateView(void) const
{
    if (isViewOutOfDate())
    {
        updateViewImpl();
    }
}

//-----------------------------------------------------------------------
void U2Frustum::updateViewImpl(void) const
{
    /**********************@@@@@@@@@@@@@@@@@@@@@
    // ----------------------
    // Update the view matrix
    // ----------------------

    // Get orientation from quaternion

    //if (!mCustomViewMatrix)
    if (true)
    {
        U2Matrix3 rot;
        const U2Quaternion& orientation = getOrientationForViewUpdate();
        const U2Vector3& position = getPositionForViewUpdate();

        mViewMatrix = U2Math::makeViewMatrix(position, orientation, mReflect? &mReflectMatrix : 0);
    }

    mRecalcView = false;

    // Signal to update frustum clipping planes
    mRecalcFrustumPlanes = true;
    // Signal to update world space corners
    mRecalcWorldSpaceCorners = true;
    
    // Signal to update frustum if oblique plane enabled,
    // since plane needs to be in view space
    if (mObliqueDepthProjection)
    {
        mRecalcFrustum = true;
    }
    */
    const U2Quaternion& orientation = getOrientationForViewUpdate();
    const U2Vector3& position = getPositionForViewUpdate();
    mViewMatrix = U2Frustum::makeViewMatrix(position, orientation, 0);
}

//-----------------------------------------------------------------------
bool U2Frustum::isViewOutOfDate(void) const
{
    /**********************@@@@@@@@@@@@@@@@@@@@@
    // Attached to node?
    if (mParentNode)
    {
        if (mRecalcView ||
            mParentNode->_getDerivedOrientation() != mLastParentOrientation ||
            mParentNode->_getDerivedPosition() != mLastParentPosition)
        {
            // Ok, we're out of date with SceneNode we're attached to
            mLastParentOrientation = mParentNode->_getDerivedOrientation();
            mLastParentPosition = mParentNode->_getDerivedPosition();
            mRecalcView = true;
        }
    }
    // Deriving reflection from linked plane?
    if (mLinkedReflectPlane && 
        !(mLastLinkedReflectionPlane == mLinkedReflectPlane->_getDerivedPlane()))
    {
        mReflectPlane = mLinkedReflectPlane->_getDerivedPlane();
        mReflectMatrix = Math::buildReflectionMatrix(mReflectPlane);
        mLastLinkedReflectionPlane = mLinkedReflectPlane->_getDerivedPlane();
        mRecalcView = true;
    }

    return mRecalcView;
    */
    return true;
}

//-----------------------------------------------------------------------
void U2Frustum::updateFrustumPlanesImpl(void) const
{
    // -------------------------
    // Update the frustum planes
    // -------------------------
    U2Matrix4 combo = mProjMatrix * mViewMatrix;

    mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.x = combo[3][0] + combo[0][0];
    mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.y = combo[3][1] + combo[0][1];
    mFrustumPlanes[FRUSTUM_PLANE_LEFT].normal.z = combo[3][2] + combo[0][2];
    mFrustumPlanes[FRUSTUM_PLANE_LEFT].d = combo[3][3] + combo[0][3];

    mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.x = combo[3][0] - combo[0][0];
    mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.y = combo[3][1] - combo[0][1];
    mFrustumPlanes[FRUSTUM_PLANE_RIGHT].normal.z = combo[3][2] - combo[0][2];
    mFrustumPlanes[FRUSTUM_PLANE_RIGHT].d = combo[3][3] - combo[0][3];

    mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.x = combo[3][0] - combo[1][0];
    mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.y = combo[3][1] - combo[1][1];
    mFrustumPlanes[FRUSTUM_PLANE_TOP].normal.z = combo[3][2] - combo[1][2];
    mFrustumPlanes[FRUSTUM_PLANE_TOP].d = combo[3][3] - combo[1][3];

    mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.x = combo[3][0] + combo[1][0];
    mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.y = combo[3][1] + combo[1][1];
    mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].normal.z = combo[3][2] + combo[1][2];
    mFrustumPlanes[FRUSTUM_PLANE_BOTTOM].d = combo[3][3] + combo[1][3];

    mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.x = combo[3][0] + combo[2][0];
    mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.y = combo[3][1] + combo[2][1];
    mFrustumPlanes[FRUSTUM_PLANE_NEAR].normal.z = combo[3][2] + combo[2][2];
    mFrustumPlanes[FRUSTUM_PLANE_NEAR].d = combo[3][3] + combo[2][3];

    mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.x = combo[3][0] - combo[2][0];
    mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.y = combo[3][1] - combo[2][1];
    mFrustumPlanes[FRUSTUM_PLANE_FAR].normal.z = combo[3][2] - combo[2][2];
    mFrustumPlanes[FRUSTUM_PLANE_FAR].d = combo[3][3] - combo[2][3];

    // Renormalise any normals which were not unit length
    for(int i=0; i<6; i++ ) 
    {
        u2real length = mFrustumPlanes[i].normal.normalise();
        mFrustumPlanes[i].d /= length;
    }

    mRecalcFrustumPlanes = false;
}
//-----------------------------------------------------------------------
void U2Frustum::updateFrustumPlanes(void) const
{
    updateView();
    updateFrustum();

    if (mRecalcFrustumPlanes)
    {
        updateFrustumPlanesImpl();
    }
}
//-----------------------------------------------------------------------
void U2Frustum::updateWorldSpaceCornersImpl(void) const
{
    U2Matrix4 eyeToWorld = mViewMatrix.inverseAffine();

    // Note: Even though we can dealing with general projection matrix here,
    //       but because it's incompatibly with infinite far plane, thus, we
    //       still need to working with projection parameters.

    // Calc neaRealr plane corners
    u2real nearLeft, nearRight, nearBottom, nearTop;
    calcProjectionParameters(nearLeft, nearRight, nearBottom, nearTop);

    // Treat infinite fardist as some arbitrary far value
    u2real farDist = (mFarDist == 0) ? 100000 : mFarDist;

    // Calc far palne corners
    u2real radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1;
    u2real farLeft = nearLeft * radio;
    u2real farRight = nearRight * radio;
    u2real farBottom = nearBottom * radio;
    u2real farTop = nearTop * radio;

    // near
    mWorldSpaceCorners[0] = eyeToWorld.transformAffine(U2Vector3(nearRight, nearTop,    -mNearDist));
    mWorldSpaceCorners[1] = eyeToWorld.transformAffine(U2Vector3(nearLeft,  nearTop,    -mNearDist));
    mWorldSpaceCorners[2] = eyeToWorld.transformAffine(U2Vector3(nearLeft,  nearBottom, -mNearDist));
    mWorldSpaceCorners[3] = eyeToWorld.transformAffine(U2Vector3(nearRight, nearBottom, -mNearDist));
    // far
    mWorldSpaceCorners[4] = eyeToWorld.transformAffine(U2Vector3(farRight,  farTop,     -farDist));
    mWorldSpaceCorners[5] = eyeToWorld.transformAffine(U2Vector3(farLeft,   farTop,     -farDist));
    mWorldSpaceCorners[6] = eyeToWorld.transformAffine(U2Vector3(farLeft,   farBottom,  -farDist));
    mWorldSpaceCorners[7] = eyeToWorld.transformAffine(U2Vector3(farRight,  farBottom,  -farDist));


    mRecalcWorldSpaceCorners = false;
}
//-----------------------------------------------------------------------
void U2Frustum::updateWorldSpaceCorners(void) const
{
    updateView();

    if (mRecalcWorldSpaceCorners)
    {
        updateWorldSpaceCornersImpl();
    }
}

//-----------------------------------------------------------------------
void U2Frustum::updateVertexData(void) const
{
    if (mRecalcVertexData)
    {
        if (mVertexData.vertexBufferBinding->getBufferCount() <= 0)
        {
            // Initialise vertex & index data
            mVertexData.vertexDeclaration->addElement(0, 0, VET_FLOAT3, VES_POSITION);
            mVertexData.vertexCount = 32;
            mVertexData.vertexStart = 0;
            mVertexData.vertexBufferBinding->setBinding( 0,
                U2HardwareBufferManager::getSingleton().createVertexBuffer(
                sizeof(float)*3, 32, U2HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY) );
        }

        // Note: Even though we can dealing with general projection matrix here,
        //       but because it's incompatibly with infinite far plane, thus, we
        //       still need to working with projection parameters.

        // Calc near plane corners
        u2real vpLeft, vpRight, vpBottom, vpTop;
        calcProjectionParameters(vpLeft, vpRight, vpBottom, vpTop);

        // Treat infinite fardist as some arbitrary far value
        u2real farDist = (mFarDist == 0) ? 100000 : mFarDist;

        // Calc far plane corners
        u2real radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1;
        u2real farLeft = vpLeft * radio;
        u2real farRight = vpRight * radio;
        u2real farBottom = vpBottom * radio;
        u2real farTop = vpTop * radio;

        // Calculate vertex positions (local)
        // 0 is the origin
        // 1, 2, 3, 4 are the points on the near plane, top left first, clockwise
        // 5, 6, 7, 8 are the points on the far plane, top left first, clockwise
        U2HardwareVertexBufferSharedPtr vbuf = mVertexData.vertexBufferBinding->getBuffer(0);
        float* pFloat = static_cast<float*>(vbuf->lock(U2HardwareBuffer::HBL_DISCARD));

        // near plane (remember frustum is going in -Z direction)
        *pFloat++ = vpLeft;  *pFloat++ = vpTop;    *pFloat++ = -mNearDist;
        *pFloat++ = vpRight; *pFloat++ = vpTop;    *pFloat++ = -mNearDist;

        *pFloat++ = vpRight; *pFloat++ = vpTop;    *pFloat++ = -mNearDist;
        *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;

        *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
        *pFloat++ = vpLeft;  *pFloat++ = vpBottom; *pFloat++ = -mNearDist;

        *pFloat++ = vpLeft;  *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
        *pFloat++ = vpLeft;  *pFloat++ = vpTop;    *pFloat++ = -mNearDist;

        // far plane (remember frustum is going in -Z direction)
        *pFloat++ = farLeft;  *pFloat++ = farTop;    *pFloat++ = -farDist;
        *pFloat++ = farRight; *pFloat++ = farTop;    *pFloat++ = -farDist;

        *pFloat++ = farRight; *pFloat++ = farTop;    *pFloat++ = -farDist;
        *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist;

        *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist;
        *pFloat++ = farLeft;  *pFloat++ = farBottom; *pFloat++ = -farDist;

        *pFloat++ = farLeft;  *pFloat++ = farBottom; *pFloat++ = -farDist;
        *pFloat++ = farLeft;  *pFloat++ = farTop;    *pFloat++ = -farDist;

        // Sides of the pyramid
        *pFloat++ = 0.0f;    *pFloat++ = 0.0f;   *pFloat++ = 0.0f;
        *pFloat++ = vpLeft;  *pFloat++ = vpTop;  *pFloat++ = -mNearDist;

        *pFloat++ = 0.0f;    *pFloat++ = 0.0f;   *pFloat++ = 0.0f;
        *pFloat++ = vpRight; *pFloat++ = vpTop;    *pFloat++ = -mNearDist;

        *pFloat++ = 0.0f;    *pFloat++ = 0.0f;   *pFloat++ = 0.0f;
        *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;

        *pFloat++ = 0.0f;    *pFloat++ = 0.0f;   *pFloat++ = 0.0f;
        *pFloat++ = vpLeft;  *pFloat++ = vpBottom; *pFloat++ = -mNearDist;

        // Sides of the box

        *pFloat++ = vpLeft;  *pFloat++ = vpTop;  *pFloat++ = -mNearDist;
        *pFloat++ = farLeft;  *pFloat++ = farTop;  *pFloat++ = -farDist;

        *pFloat++ = vpRight; *pFloat++ = vpTop;    *pFloat++ = -mNearDist;
        *pFloat++ = farRight; *pFloat++ = farTop;    *pFloat++ = -farDist;

        *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
        *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist;

        *pFloat++ = vpLeft;  *pFloat++ = vpBottom; *pFloat++ = -mNearDist;
        *pFloat++ = farLeft;  *pFloat++ = farBottom; *pFloat++ = -farDist;


        vbuf->unlock();

        mRecalcVertexData = false;
    }
}

//-----------------------------------------------------------------------
void U2Frustum::getRenderOperation(U2RenderOperation& op) 
{
    updateVertexData();
    op.operationType = U2RenderOperation::OT_LINE_LIST;
    op.useIndexes = false;
    op.useGlobalInstancingVertexBufferIsAvailable = false;
    op.vertexData = &mVertexData;
}

//-----------------------------------------------------------------------
const U2Matrix4& U2Frustum::getViewMatrix(void) const
{
    updateView();

    return mViewMatrix;
}

//-----------------------------------------------------------------------
const U2Vector3& U2Frustum::getPositionForViewUpdate(void) const
{
    return mLastParentPosition;
}
//-----------------------------------------------------------------------
const U2Quaternion& U2Frustum::getOrientationForViewUpdate(void) const
{
    return mLastParentOrientation;
}

//---------------------------------------------------------------------
U2Matrix4 U2Frustum::makeViewMatrix(const U2Vector3& position, const U2Quaternion& orientation, 
                                 const U2Matrix4* reflectMatrix)
{
    U2Matrix4 viewMatrix;

    // View matrix is:
    //
    //  [ Lx  Uy  Dz  Tx  ]
    //  [ Lx  Uy  Dz  Ty  ]
    //  [ Lx  Uy  Dz  Tz  ]
    //  [ 0   0   0   1   ]
    //
    // Where T = -(Transposed(Rot) * Pos)

    // This is most efficiently done using 3x3 Matrices
    U2Matrix3 rot;
    orientation.ToRotationMatrix(rot);

    // Make the translation relative to new axes
    U2Matrix3 rotT = rot.Transpose();
    U2Vector3 trans = -rotT * position;

    // Make final matrix
    viewMatrix = U2Matrix4::IDENTITY;
    viewMatrix = rotT; // fills upper 3x3
    viewMatrix[0][3] = trans.x;
    viewMatrix[1][3] = trans.y;
    viewMatrix[2][3] = trans.z;

    // Deal with reflections
    if (reflectMatrix)
    {
        viewMatrix = viewMatrix * (*reflectMatrix);
    }

    return viewMatrix;

}